package net.kem.utils.directorywatcher;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.regex.Pattern;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;



public class FileZipper extends DirectoryWatcher {
	private ZipFileDestinator _zipFileDestinator;
	private boolean _removeSourceFile;

	/**
	 * Creates new FileZipper that used the default {@link #DATE_TIME_NAMED} ZipFileDestinator.
	 * By default, original non-compressed file will be removed once it is compressed successfully.
	 * Use {@link #setRemoveSourceFile(boolean)} to change the default behavior.
	 * @param directoryToWatch directory to be watched.
	 * @param pattern Pattern to filter files in the directory. If <em>null</em>, then the default Pattern will be used.
	 * The default Pattern allows all files.
	 * @see #FileZipper(ZipFileDestinator)
	 */
	public FileZipper(String directoryToWatch, Pattern pattern) {
		this(new File(directoryToWatch), DATE_TIME_NAMED, pattern);
	}


	/**
	 * Creates new FileZipper that the given ZipFileDestinator to generate name of the newly created compressed file.
	 * By default, original non-compressed file will be removed once it is compressed successfully.
	 * Use {@link #setRemoveSourceFile(boolean)} to change the default behavior.
	 * @param directoryToWatch directory to be watched.
	 * @param zipFileDestinator ZipFileDestinator to generate name of the newly created compressed file.
	 * @param pattern Pattern to filter files in the directory. If <em>null</em>, then the default Pattern will be used.
	 * The default Pattern allows all files.
	 * If <em>null</em>, then the default {@link #DATE_TIME_NAMED} ZipFileDestinator will be used.
	 */
	public FileZipper(File directoryToWatch, ZipFileDestinator zipFileDestinator, Pattern pattern) {
		super(directoryToWatch, pattern);
		_zipFileDestinator = zipFileDestinator == null? DATE_TIME_NAMED: zipFileDestinator;
		setRemoveSourceFile(true);
	}


	@Override
	protected File processFile(File file) {
		File zippedFile;
		try {
			zippedFile = _zipFileDestinator.getDestinationFile(file);
			ZipOutputStream zout = new ZipOutputStream(new BufferedOutputStream(new FileOutputStream(zippedFile), 1024*1024));
			BufferedInputStream bis = new BufferedInputStream(new FileInputStream(file), 1024*1024);
			// zout.setLevel(5);
			try {
				String zipEntryName = zippedFile.getName();
				if(zipEntryName.endsWith(".zip")) {
					zipEntryName = zipEntryName.substring(0, zipEntryName.length()-4);
				} else {
					if(zipEntryName.endsWith(".gz")) {
						zipEntryName = zipEntryName.substring(0, zipEntryName.length()-3);
					}
				}
				if(_isDebugEnabled) {
					LOG.debug("FileZipper: Compressing " + file.getName() + " to " + zippedFile.getName() + " (" + zipEntryName + ")");
				}
				// Put a ZIP entry.
				ZipEntry ze = new ZipEntry(zipEntryName);
				zout.putNextEntry(ze);
				// Copy data to ZIP stream.
				byte[] buffer = new byte[1024*1024];
				for (int c = bis.read(buffer); c != -1; c = bis.read(buffer)) {
					zout.write(buffer, 0, c);
				}
			} finally {
				if(bis != null) bis.close();
				if(zout != null) zout.close();
			}

			if(_removeSourceFile) {
				if(!file.delete()) {
					LOG.error("FileZipper: File " + file.getName() + " could not be deleted.");
				}
			}
		} catch (IOException e) {
			LOG.error("FileZipper: Error", e);
			zippedFile = null;
		}
		return zippedFile;
	}



	public boolean isRemoveSourceFile() {
		return _removeSourceFile;
	}

	public void setRemoveSourceFile(boolean removeSourceFile) {
		_removeSourceFile = removeSourceFile;
	}




	/**
	 * Generates destination zip File instance based on the given non-compressed file name.
	 * @author Evgeny.Kurtser
	 */
	public interface ZipFileDestinator {
		/**
		 * Generates destination zip File instance based on the given non-compressed file name.
		 * @param sourceFile non-compressed file instance.
		 * @return zip File instance based on the given non-compressed file name.
		 */
		File getDestinationFile(File sourceFile);
	}
	/**
	 * Generates destination zip File instance with the following file name pattern:
	 * yyyy-MM-dd_HH_mm_sourceFileName.zip
	 */
	public static final ZipFileDestinator DATE_TIME_NAMED = new ZipFileDestinator() {
		public File getDestinationFile(File sourceFile) {
			DateFormat df = new SimpleDateFormat("yyyy-MM-dd_HH_mm");
			File res = new File(sourceFile.getParentFile(), df.format(new Date())
					+ "_" + sourceFile.getName() + ".zip");
			return res;
		}
	};
	/**
	 * Generates destination zip File instance with the following file name pattern:
	 * yyyy-MM-dd_sourceFileName.zip
	 */
	public static final ZipFileDestinator DATE_NAMED = new ZipFileDestinator() {
		public File getDestinationFile(File sourceFile) {
			DateFormat df = new SimpleDateFormat("yyyy-MM-dd");
			File res = new File(sourceFile.getParentFile(), df.format(new Date())
					+ "_" + sourceFile.getName() + ".zip");
			return res;
		}
	};
	/**
	 * Generates destination zip File instance with the following file name pattern:
	 * HH_mm_sourceFileName.zip
	 */
	public static final ZipFileDestinator TIME_NAMED = new ZipFileDestinator() {
		public File getDestinationFile(File sourceFile) {
			DateFormat df = new SimpleDateFormat("HH_mm");
			File res = new File(sourceFile.getParentFile(), df.format(new Date())
					+ "_" + sourceFile.getName() + ".zip");
			return res;
		}
	};
}